/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package fr.uha.ensisa.kfetmanager.customer;

import fr.uha.ensisa.kfetmanager.category.CustomerCategory;
import fr.uha.ensisa.kfetmanager.product.Product;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import javax.ejb.LocalBean;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.validation.ValidationException;

/**
 *
 * @author JC
 */
@Stateless
@LocalBean
public class CustomerManagerBean implements CustomerManagerRemote {
    @PersistenceContext(unitName="kfet-manager-ejbPU")
    private EntityManager em;


    @Override
    public Customer createCustomer(Customer customer, String firstName, String lastName, String nickname, boolean cotisant, boolean gerantKfet, double balance, CustomerCategory category) {
        if (customer == null) throw new ValidationException("L'objet customer est null");
        if (nickname == null) throw new ValidationException("L'objet nickname est null");
        // If no nickname is provided, make it equal to the last name
        if (nickname.equals("")) nickname = lastName;
        if (nickname.equals("")) throw new ValidationException("Le nom est vide");

        if (this.exists(nickname)) throw new ValidationException("Un client existe déjà avec ce nom");

        customer.setFirstName(firstName);
        customer.setLastName(lastName);
        customer.setNickname(nickname);
        customer.setCotisant(cotisant);
        customer.setGerantKfet(gerantKfet);
        customer.setBalance(balance);
        customer.setCreationDate(new Date());
        customer.setCategory(category);

        em.persist(customer);
        
        return customer;
    }

    @Override
    public Customer updateCustomer(Customer customer, String firstName, String lastName, String nickname, boolean cotisant, boolean gerantKfet, CustomerCategory category) {
        if (customer == null) throw new ValidationException("L'objet customer est null");
        if (nickname == null) throw new ValidationException("L'objet nickname est null");
        // If no nickname is provided, make it equal to the last name
        if (nickname.equals("")) nickname = lastName;
        if (nickname.equals("")) throw new ValidationException("Le nom est vide");

        if (!nickname.equals(customer.getNickname()) && this.exists(nickname)) throw new ValidationException("Un client existe déjà avec ce nom");

        customer.setFirstName(firstName);
        customer.setLastName(lastName);
        customer.setNickname(nickname);
        customer.setCotisant(cotisant);
        customer.setGerantKfet(gerantKfet);
        customer.setCategory(category);

        em.merge(customer);

        return customer;
    }

    @Override
    public void deleteCustomer(Customer customer) {
        if (customer == null) throw new ValidationException("L'objet customer est null");

        em.remove(em.merge(customer));
    }

    @Override
    /**
     * @returns The Customer object with the specified id,
     *          or null if an exception was thrown while finding this object,
     *          or if there is no object with this id.
     */
    public Customer getCustomer(Long id) {
        if (id == null) throw new ValidationException("L'objet ID est null");
        try {
            return (Customer)em.find(Customer.class, id);
        } catch(Exception e) {
            return null;
        }
    }

    @Override
    public List<Customer> getCustomers() {
        Query query = em.createQuery("SELECT c FROM Customer c");
        List<Customer> customers = query.getResultList();

        return customers;
    }

    @Override
    /**
     * @returns a List of Customer objects whose nicknames or last names start with the String filter (case insensitive).
     */
    public List<Customer> getCustomers(String filter) {
        Query query = em.createQuery("SELECT c FROM Customer c WHERE lower(c.nickname) LIKE :nick OR lower(c.lastName) LIKE :nick")
                .setParameter("nick", filter.toLowerCase() + "%");
        List<Customer> customers = query.getResultList();

        return customers;
    }

    @Override
    public Customer buyProduct(Customer customer, Product product) throws Exception {
        return this.buyProduct(customer, product, 1);
    }


    @Override
    public Customer buyProduct(Customer customer, Product product, int quantity) throws Exception {
	if (customer == null) throw new ValidationException("L'objet customer est null");
        if (product == null) throw new ValidationException("L'objet product est null");

        BigDecimal solde = new BigDecimal(customer.getBalance());
	BigDecimal price = new BigDecimal(product.getPrice());
        BigDecimal minimumBalance = customer.isGerantKfet() ? new BigDecimal(-20.0) : new BigDecimal(0.0);
	BigDecimal subPrice = price.multiply(new BigDecimal(quantity));

	BigDecimal newBalance = solde.subtract(subPrice);

        if (newBalance.compareTo(minimumBalance) == -1)
	    throw new ValidationException("Pas assez de solde");

        customer.setBalance(newBalance.doubleValue());

        em.merge(customer);

        // TODO: Transaction logging

        return customer;
    }

    @Override
    public Customer creditAccount(Customer customer, double amount) {
        if (customer == null) throw new ValidationException("L'objet customer est null");
        
        customer.setBalance(customer.getBalance() + amount);

        em.merge(customer);
        return customer;
    }

    @Override
    public Iterator<Customer> iterator() {
        return this.getCustomers().iterator();
    }

    @Override
    public boolean exists(String nickname) {
        Query query = em.createQuery("SELECT c FROM Customer c WHERE lower(c.nickname) LIKE :nick")
                .setParameter("nick", nickname.toLowerCase());
        List<Customer> customers = query.getResultList();

        if (customers.size() != 0) return true;
        return false;
    }
}
